package ai.fabu.dataplatform.service.impl;

import ai.fabu.dataplatform.bean.bagdata.BagData;
import ai.fabu.dataplatform.bean.framedata.GetBinData;
import ai.fabu.dataplatform.bean.groupdata.GroupData;
import ai.fabu.dataplatform.bean.help.Position;
import ai.fabu.dataplatform.bean.metadata.GetMetadata;
import ai.fabu.dataplatform.bean.util.IndexData;
import ai.fabu.dataplatform.bean.util.Pair;
import ai.fabu.dataplatform.constant.ConstantName;
import ai.fabu.dataplatform.constant.ResultCode;
import ai.fabu.dataplatform.help.Result;
import ai.fabu.dataplatform.service.ApiForFrontService;
import ai.fabu.dataplatform.service.ApiForTaskService;
import ai.fabu.dataplatform.service.ApiService;
import ai.fabu.dataplatform.utils.*;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.mongodb.MongoException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.UncategorizedMongoDbException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

import javax.validation.constraints.NotBlank;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.Callable;

import static ai.fabu.dataplatform.constant.ConstantName.*;
import static ai.fabu.dataplatform.constant.ConstantName.RECORD;
import static ai.fabu.dataplatform.constant.ConstantValue.*;
import static ai.fabu.dataplatform.utils.HttpUtil.doGet;

@Service
@Slf4j
public class ApiForTaskServiceImpl implements ApiForTaskService {

    @Autowired
    private ApiService apiService;
    @Autowired
    private ApiForFrontService apiForFrontService;

    @Value("${mileage.point.interval}")
    private int mileagePointInterval;
    @Value("${map.point.interval}")
    private int mapPointInterval;
    @Value("${bag.data.save.path}")
    private String bagSavePath;


    @Retryable(
            value = {MongoException.class, UncategorizedMongoDbException.class},
            maxAttempts = 50,
            backoff = @Backoff(delay = 100L, maxDelay = 5000L, random = true)
    )
//    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result generateBagRecordByName(@NotBlank String groupName) {
        String methodName = new Exception().getStackTrace()[0].getMethodName();
        log.info("{}: group name: {}", methodName, groupName);
        Result result = new Result();
        IndexData indexData = new IndexData();
        indexData.setGroupName(groupName);
        Result<List<GetMetadata>> metadataResult = apiService.getMetadata(indexData, GROUP_TYPE, RECORD);
        if(metadataResult.getCode() != ResultCode.SUCCESS.getCode()){
            log.error("{}: query group metadata error", methodName);
            result.returnWithOutValue(metadataResult);
            return result;
        }
        // get group record
        GetMetadata getMetadata = metadataResult.getData().get(0);
        if(getMetadata.getData().isEmpty()){
            log.warn("{}: group : {}, record exist", methodName, groupName);
            result.returnWithoutValue(ResultCode.SUCCESS);
            return result;
        }
        byte[] recordBytes = getMetadata.getData().get(0).getValue();
        String recordString = new String(recordBytes);
        List<Object> recordList = JSONUtil.convertJSonString2List(recordString);

        // get bag list
        IndexData bagIndexData = new IndexData();
        bagIndexData.setGroupName(groupName);

        Result<List<GetMetadata>> bagMetadataResult = apiService.getMetadata(bagIndexData, BAG_TYPE, ConstantName.BEGIN_TIME, ConstantName.END_TIME);
        if(bagMetadataResult.getCode() != ResultCode.SUCCESS.getCode()){
            log.error("{}: query bag metadata error", methodName);
            result.returnWithOutValue(bagMetadataResult);
            return result;
        }

        List<GetMetadata> bagGetMetadataList = bagMetadataResult.getData();

        List<Callable<Result>> callableList = new ArrayList<>();
        bagGetMetadataList.forEach(metadata -> callableList.add(() -> {
            String bagName = metadata.getIndexData().getBagName();
            List<Pair<String, byte[]>> pairList = metadata.getData();
            long bagBeginTime = 0L, bagEndTime = 0L;
            for (Pair<String, byte[]> pair : pairList) {
                if (pair.getKey().equals(ConstantName.BEGIN_TIME))
                    bagBeginTime = Long.parseLong(new String(pair.getValue()));
                else if (pair.getKey().equals(ConstantName.END_TIME))
                    bagEndTime = Long.parseLong(new String(pair.getValue()));
            }

            List<Object> bagRecordList = new ArrayList<>();
            for (Object object : recordList) {
                Object beginObject = ((Map) object).get(TIMESTAMP);
                if(beginObject == null)  continue;
                long beginTime = 0;
                if(beginObject.getClass() == String.class) {
                    if(((String) beginObject).isEmpty())  continue;
                    beginTime = Long.parseLong(((String) beginObject));
                }
                else {
                    beginTime = (long) beginObject;
                }
                long endTime = beginTime + 59999;  // 60s = 1min = 60000ms

                // interval intersect
                if (bagBeginTime <= beginTime && beginTime <= bagEndTime
                        || bagBeginTime <= endTime && endTime <= bagEndTime
                        || beginTime <= bagBeginTime && bagBeginTime <= endTime) {
                    bagRecordList.add(object);
                }
            }
            IndexData bagIndex = new IndexData();
            bagIndex.setGroupName(groupName);
            bagIndex.setBagName(bagName);
            return apiService.saveMetadata(bagIndex, BagData.class, BAG_TYPE, new Pair<>(RECORD, JSONUtil.convertList2Bytes(bagRecordList)));
        }));

        try {
            ThreadManager.invokeAll(callableList);
        } catch (InterruptedException e) {
            throw new MongoException("thread interrupt and retry again");
        }
        result.returnWithoutValue(ResultCode.SUCCESS);
        return result;
    }

    @Retryable(
            value = {MongoException.class, UncategorizedMongoDbException.class},
            maxAttempts = 50,
            backoff = @Backoff(delay = 100L, maxDelay = 5000L, random = true)
    )
//    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result generateBagGpsByName(@NotBlank String groupName) {
        String methodName = new Exception().getStackTrace()[0].getMethodName();
        log.info("{}: group name: {}", methodName, groupName);
        Result result = new Result();
        IndexData bagIndexData = new IndexData();
        bagIndexData.setGroupName(groupName);
        Result<List<IndexData>> bagIndexResult = apiService.getIndex(bagIndexData, BAG_TYPE);
        if(bagIndexResult.getCode() != ResultCode.SUCCESS.getCode()){
            log.error("{}: get bag index error", methodName);
            result.returnWithOutValue(bagIndexResult);
            return result;
        }
        List<IndexData> bagIndexDataList = bagIndexResult.getData();

        for (IndexData indexData : bagIndexDataList) {
            indexData.setTopicName(TOPIC_ROADSTAR_LOCALIZATION);
            indexData.setFrameNo(FRAME_FIRST_FRAME);
            Result<List<GetBinData>> binDataResult = apiService.getBinData(indexData);
            if(binDataResult.getCode() != ResultCode.SUCCESS.getCode()){
                log.error("{}: get bin data error. index: {}", methodName, indexData);
                result.returnWithOutValue(binDataResult);
                return result;
            }
            List<GetBinData> binDataList = binDataResult.getData();
            if(binDataList.isEmpty()){
                log.warn("{}: bin data is empty, index: {}", methodName, indexData);
                apiService.saveMetadata(indexData, BagData.class, BAG_TYPE, new Pair<>(GPS, "[]".getBytes()));
                continue;
            }

            byte[] bytes = binDataList.get(0).getBinData();
            List<Position> pointList = FileUtil.readLocalizationBinFileByBytes(bytes, BAG_GPS_INTERVAL);
            pointList = utm2BaiduGps(pointList);
            Result<Long> bagMetadataResult = apiService.saveMetadata(indexData, BagData.class, BAG_TYPE, new Pair<>(GPS, pointList.toString().getBytes()));
            if(bagMetadataResult.getCode() != ResultCode.SUCCESS.getCode()){
                log.error("{}: save bag metadata error, index: {}", methodName, indexData);
                result.returnWithOutValue(bagMetadataResult);
                return result;
            }
        }
        result.returnWithoutValue(ResultCode.SUCCESS);
        return result;
    }


    @Retryable(
            value = {MongoException.class, UncategorizedMongoDbException.class},
            maxAttempts = 50,
            backoff = @Backoff(delay = 100L, maxDelay = 5000L, random = true)
    )
    @Override
    public Result generateGroupMapAndMileageByName(@NotBlank String groupName) {
        String methodName = new Exception().getStackTrace()[0].getMethodName();
        log.info("{}: group name: {}", methodName, groupName);
        Result result = new Result();
        IndexData bagIndexData = new IndexData();
        bagIndexData.setGroupName(groupName);
        Result<List<GetMetadata>> metadataResult = apiService.getMetadata(bagIndexData, BAG_TYPE, GPS);
        if(metadataResult.getCode() != ResultCode.SUCCESS.getCode()){
            log.error("{}: group name: {}", methodName, groupName);
            result.returnWithOutValue(metadataResult);
            return result;
        }


        List<GetMetadata> getMetadataList = metadataResult.getData();
        List<Position> baiduGpsList = new ArrayList<>();
        List<List<Position>> mileagebaiduGpsLists = new ArrayList<>();
        final int mapDet = mapPointInterval / BAG_GPS_INTERVAL;
        final int mileageDet = mileagePointInterval / BAG_GPS_INTERVAL;
        getMetadataList.forEach(metadata -> {
            List<Position> mileagebaiduGpsList = new ArrayList<>();
            if(metadata.getData().isEmpty()){
                mileagebaiduGpsLists.add(mileagebaiduGpsList);
                return;
            };  // no gps
            Pair<String, byte[]> gpsBytes = metadata.getData().get(0);
            List<Object> objectList = JSONUtil.convertJSonString2List(new String(gpsBytes.getValue()));
            for(int i = 0; i < objectList.size(); ++i){
//                log.info("{}: {}", methodName, objectList.get(i));
                Map<String, BigDecimal> map = (Map<String, BigDecimal>) objectList.get(i);
                if(i + 1 == objectList.size()){
                    baiduGpsList.add(new Position(map.get("x").doubleValue(), map.get("y").doubleValue()));
                    mileagebaiduGpsList.add(new Position(map.get("x").doubleValue(), map.get("y").doubleValue()));
                    break;
                }
                if(i % mapDet == 0)
                    baiduGpsList.add(new Position(map.get("x").doubleValue(), map.get("y").doubleValue()));
                if(i % mileageDet == 0)
                    mileagebaiduGpsList.add(new Position(map.get("x").doubleValue(), map.get("y").doubleValue()));
            }
            mileagebaiduGpsLists.add(mileagebaiduGpsList);
        });



        String saveFile = bagSavePath + groupName + "/localization.png";
        if(baiduGpsList.isEmpty()){
            log.warn("{}: no image to generate (no gps)", methodName);
        }

        List<Position> baiduGpsListGap = new ArrayList<>();
        int gap = baiduGpsList.size() / 200 + 1;
        if (gap != 1) {
            for (int i = 0; i < baiduGpsList.size() - 1; i += gap) {
                baiduGpsListGap.add(baiduGpsList.get(i));
            }
            baiduGpsListGap.add(baiduGpsList.get(baiduGpsList.size() - 1));
        } else {
            baiduGpsListGap = baiduGpsList;
        }
        int mapResult = initMap(baiduGpsListGap, saveFile);
        double groupMileage = calculateMileage(mileagebaiduGpsLists, getMetadataList, groupName);
        if (mapResult != 0) {
            throw new RuntimeException("write image to file error!");
        }
        IndexData groupIndexData = new IndexData();
        groupIndexData.setGroupName(groupName);
        Result<Long> groupResult = apiService.saveMetadata(groupIndexData, GroupData.class, GROUP_TYPE, new Pair<>(LOCALIZATION_IMAGE_PATH, saveFile.getBytes()), new Pair<>(MILEAGE, Double.toString(groupMileage).getBytes()));

        if(groupResult.getData() == 0){
            log.error("{}: group data is null", methodName);
            throw new RuntimeException("group data is null");
        }
        log.info("{}: static map and mileage generate successful", methodName);
        result.returnWithoutValue(ResultCode.SUCCESS);
        return result;
    }




    private int initMap(List<Position> baiduGpsList, String saveFile) {
        if(baiduGpsList == null)  return -1;
        StringBuilder sb = new StringBuilder();
        int count = 0;
        for (Position position : baiduGpsList) {
            count++;
            sb.append(position.getX()).append(",").append(position.getY());
            if (count != baiduGpsList.size()) {
                sb.append(";");
            }
        }
        String newXY = sb.toString();
        if (newXY.length() == 0) {
            log.warn("newXY error");
        }
        saveBaiduMap(saveFile, newXY);
        return 0;
    }

    private void saveBaiduMap(String saveFile, String baiduPosListStr) {
        try {
            String url = "http://api.map.baidu.com/staticimage/v2";
            StringBuilder sb = new StringBuilder();
            sb.append(url);
            sb.append("?ak=" + BAIDU_MAP_KEY);
            Map<String, String> map = new HashMap<>(10);
            map.put("bbox", getBboxStr(baiduPosListStr));
            map.put("width", "100");
            map.put("height", "100");
            //这种效果粒度较大
//            map.put("center", getCenterStr(baiduPosListStr));
//            map.put("zoom", "13");

            map.put("paths", baiduPosListStr);
            map.put("pathStyles", "0xff0000,5,1");
            map.forEach((key, value) -> sb.append("&").append(key).append("=").append(value));

//            for (String key : map.keySet()) {
//                sb.append("&" + key + "=" + map.get(key));
//                sb.append("&").append(key).append("=").append(map.get(key));
//            }
            String newURL = sb.toString();
            if(baiduPosListStr.length() == 0){
                newURL = "http://api.map.baidu.com/staticimage/v2?ak=eS0kBaAOFVFuKGoN1SBN10AdrGhl0t8D&width=100&height=100&center=杭州&markers=&zoom=11&markerStyles=s,A,0xff0000";
            }
            UrlResource.saveUrlFile(newURL, saveFile);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("image save fail");
        }
    }

//    private String getCenterStr(String baiduPosListStr) {
//        if (!baiduPosListStr.contains(";")) {
//            return baiduPosListStr;
//        }
//        String[] strs = baiduPosListStr.split(";");
//        List<Double> lonList = new ArrayList<>();
//        List<Double> latList = new ArrayList<>();
//        for (int i = 0; i < strs.length; i++) {
//            double lon = Double.valueOf(strs[i].split(",")[0]);
//            lonList.add(lon);
//            double lat = Double.valueOf(strs[i].split(",")[1]);
//            latList.add(lat);
//        }
//        Collections.sort(lonList);
//        Collections.sort(latList);
//        double latCenter = (latList.get(0) + latList.get(latList.size() - 1)) / 2;
//        double lonCenter = (lonList.get(0) + lonList.get(lonList.size() - 1)) / 2;
//        String str = String.valueOf(lonCenter) + "," + String.valueOf(latCenter);
//        return str;
//    }

    @Override
    public List<Position> utm2BaiduGps(List<Position> utmList){
        List<Position> gps = utm2GPS(utmList);
        if (gps == null) {
            log.error("utm2BaiduGps: utm2GPS error");
            return null;
        }

        List<Position> baiduGps = new ArrayList<>();
        int size = gps.size();
        int batch = (int) Math.ceil(size / 80.0);
        for (int i = 0; i < batch; i++) {
            int end = (i + 1) * 80;
            if (i == batch - 1) {
                end = size;
            }
            baiduGps.addAll(gPS2baidu(gps.subList(i * 80, end)));
        }
        return baiduGps;
    }

    private String getBboxStr(String baiduPosListStr) {
        //todo 单点的时候难以确定 至少有一个点！
//        minX,minY;maxX,maxY
        if (!baiduPosListStr.contains(";")) {
            return baiduPosListStr;
        }
        String[] strs = baiduPosListStr.split(";");
        List<Double> lonList = new ArrayList<>();
        List<Double> latList = new ArrayList<>();

        for (String str: strs) {
            double lon = Double.valueOf(str.split(",")[0]);
            lonList.add(lon);
            double lat = Double.valueOf(str.split(",")[1]);
            latList.add(lat);
        }
        Collections.sort(lonList);
        Collections.sort(latList);
        double minX = lonList.get(0);
        double minY = latList.get(0);
        double maxX = latList.get(latList.size() - 1);
        double maxY = lonList.get(lonList.size() - 1);
        String str = String.valueOf(minX) + "," + String.valueOf(minY) + ";"
                + String.valueOf(maxX) + "," + String.valueOf(maxY);
        return str;
    }

    private List<Position> utm2GPS(List<Position> utmList) {
        List<Position> gpsList = new ArrayList<>();
        for (Position position : utmList) {
            int x = (int) position.getX();
            int y = (int) position.getY();
            CoordinateConversion coordinateConversion = new CoordinateConversion();
            String utmStr = "51 R " + String.valueOf(x) + " " + String.valueOf(y);
            double[] latlon = coordinateConversion.utm2LatLon(utmStr);
            //算法输出为纬度靠前，我们改为经度靠前
            Position positionGPS = new Position(latlon[1], latlon[0]);
            gpsList.add(positionGPS);
        }
        return gpsList;
    }

    ///据说最多支持100个
    private List<Position> gPS2baidu(List<Position> gps) {
        List<Position> baiduPointList = new ArrayList<>();
        String url = "http://api.map.baidu.com/geoconv/v1/";
        StringBuilder stringBuilder = new StringBuilder();
        int count = 0;
        for (Position position : gps) {
            count++;
            stringBuilder.append(position.getX());
            stringBuilder.append(",");
            stringBuilder.append(position.getY());
            if (count != gps.size()) {
                stringBuilder.append(";");
            }
        }
        String gpsStr = stringBuilder.toString();
        Map<String, String> map = new HashMap<>(10);
        map.put("coords", gpsStr);
        map.put("from", "1");
        map.put("to", "5");
        map.put("ak", BAIDU_MAP_KEY);
        String result = doGet(url, map);
        // todo
        JSONObject jsonObject = JSONObject.parseObject(result);
        JSONArray bagsArray;
        if (jsonObject.getInteger("status") == 0) {
            bagsArray = jsonObject.getJSONArray("result");
            for (int i = 0; i < bagsArray.size(); i++) {
                JSONObject bagObject = bagsArray.getJSONObject(i);
                Position position = new Position(bagObject.getDouble("x"), bagObject.getDouble("y"));
                baiduPointList.add(position);
            }
            return baiduPointList;
        } else {
            return new ArrayList<>();
        }
    }



    @Retryable(
            value = {MongoException.class, UncategorizedMongoDbException.class},
            backoff = @Backoff(delay = 100L, maxDelay = 3000L, random = true)
    )
    private double calculateMileage(List<List<Position>> baiduGpsLists, List<GetMetadata> bagMetadataList, String groupName){
        log.info("calculateMileage: group name: {}", groupName);
        double groupMileage = 0.;
        Position last = null;

        for(int i = 0; i < baiduGpsLists.size(); ){
            List<Position> baiduGpsList = baiduGpsLists.get(i);
            String bagName = bagMetadataList.get(i).getIndexData().getBagName();
            double bagMileage = 0.;
            if(baiduGpsList.isEmpty() && last == null){
                ++i;
            }
            else if(!baiduGpsList.isEmpty()){
                if(last != null){
                    List<Position> twoPoints = new ArrayList<>();
                    twoPoints.add(last);
                    twoPoints.add(baiduGpsList.get(0));
                    groupMileage += getMileage(twoPoints);
                }
                bagMileage = getMileage(baiduGpsList);
                last = baiduGpsList.get(baiduGpsList.size()-1);
                ++i;
            }
            else{
                boolean isAllBagEmpty = true;
                for(int j = i + 1; j < baiduGpsLists.size(); ++j){
                    baiduGpsList = baiduGpsLists.get(j);
                    if(baiduGpsList.isEmpty())  continue;
                    List<Position> twoPoints = new ArrayList<>();
                    twoPoints.add(last);
                    twoPoints.add(baiduGpsList.get(0));
                    double mileage = getMileage(twoPoints);
                    last = baiduGpsList.get(baiduGpsList.size()-1);
                    groupMileage += mileage;
                    bagMileage = mileage / (j - i);
                    // i ~ j-1
                    for(int k = i; k < j; ++k){
                        IndexData bagIndexData = new IndexData();
                        bagIndexData.setGroupName(groupName);
                        bagIndexData.setBagName(bagMetadataList.get(k).getIndexData().getBagName());
                        Result<Long> longResult = apiService.saveMetadata(bagIndexData, BagData.class, BAG_TYPE, new Pair<>(MILEAGE, Double.toString(bagMileage).getBytes()));
                        if(longResult.getCode() != ResultCode.SUCCESS.getCode()){
                            log.error("calculateMileage: save bag metadata error");
                            return 0.;
                        }
                        log.info("calculateMileage: bag name: {}, mileage: {} m", bagMetadataList.get(k).getIndexData().getBagName(), bagMileage);
                    }
                    // j
                    double bagJMileage = getMileage(baiduGpsList);
                    groupMileage += bagJMileage;

                    IndexData bagIndexData = new IndexData();
                    bagIndexData.setGroupName(groupName);
                    bagIndexData.setBagName(bagMetadataList.get(j).getIndexData().getBagName());
                    Result<Long> longResult = apiService.saveMetadata(bagIndexData, BagData.class, BAG_TYPE, new Pair<>(MILEAGE, Double.toString(bagJMileage).getBytes()));
                    if(longResult.getCode() != ResultCode.SUCCESS.getCode()){
                        log.error("calculateMileage: save bag metadata error");
                        return 0.;
                    }
                    log.info("calculateMileage: bag name: {}, mileage: {} m", bagMetadataList.get(j).getIndexData().getBagName(), bagJMileage);
                    i = j + 1;
                    isAllBagEmpty = false;
                    break;
                }
                if(isAllBagEmpty){
                    for(int j = i; j < baiduGpsLists.size(); ++j){
                        IndexData bagIndexData = new IndexData();
                        bagIndexData.setGroupName(groupName);
                        bagIndexData.setBagName(bagMetadataList.get(j).getIndexData().getBagName());
                        Result<Long> longResult = apiService.saveMetadata(bagIndexData, BagData.class, BAG_TYPE, new Pair<>(MILEAGE, Double.toString(0.).getBytes()));
                        if(longResult.getCode() != ResultCode.SUCCESS.getCode()){
                            log.error("calculateMileage: save bag metadata error");
                            return 0.;
                        }
                        log.info("calculateMileage: bag name: {}, mileage: {} m", bagMetadataList.get(j).getIndexData().getBagName(), 0);
                    }
                    i = baiduGpsLists.size();
                }
                continue;
            }

            log.info("calculateMileage: bag name: {}, mileage: {} m", bagMetadataList.get(i-1).getIndexData().getBagName(), bagMileage);

            groupMileage += bagMileage;
            IndexData bagIndexData = new IndexData();
            bagIndexData.setGroupName(groupName);
            bagIndexData.setBagName(bagName);
            Result<Long> longResult = apiService.saveMetadata(bagIndexData, BagData.class, BAG_TYPE, new Pair<>(MILEAGE, Double.toString(bagMileage).getBytes()));
            if(longResult.getCode() != ResultCode.SUCCESS.getCode()){
                log.error("calculateMileage: save bag metadata error");
                return 0.;
            }
        }
        log.info("calculateMileage: group name: {}, mileage: {} m", groupName, groupMileage);
        return groupMileage;
    }





    // mileage
    /**
     * 平均半径,单位：m
     */

    private double getMileage(List<Position> baiduGpsList) {
        double mileage = 0;
        //utm转GPS
//        List<Position> gpsList = utm2GPS(utmList);
//        List<Position> gpsList = utm2BaiduGps((utmList));
        //gpsList.forEach(position -> System.out.println(position.getX()+ "," + position.getY()));
        if (baiduGpsList.isEmpty()) {
            return 0;
        }
        //轮询所有点 求和
        for (int i = 0; i < baiduGpsList.size() - 1; i++) {
            mileage += getGpsDistance(baiduGpsList.get(i), baiduGpsList.get(i + 1));
        }

        return mileage;
    }



    /**
     * 通过AB点经纬度获取距离
     * @param pointA A点(经，纬)
     * @param pointB B点(经，纬)
     * @return 距离(单位：米)
     */
    private double getGpsDistance(Position pointA, Position pointB) {
        // 经纬度（角度）转弧度。弧度用作参数，以调用Math.cos和Math.sin
        // A经弧度
        double radiansAX = Math.toRadians(pointA.getX());
        // A纬弧度
        double radiansAY = Math.toRadians(pointA.getY());
        // B经弧度
        double radiansBX = Math.toRadians(pointB.getX());
        // B纬弧度
        double radiansBY = Math.toRadians(pointB.getY());

        // 公式中“cosβ1cosβ2cos（α1-α2）+sinβ1sinβ2”的部分，得到∠AOB的cos值
        double cos = Math.cos(radiansAY) * Math.cos(radiansBY) * Math.cos(radiansAX - radiansBX)
                + Math.sin(radiansAY) * Math.sin(radiansBY);
        //System.out.println("cos = " + cos); // 值域[-1,1]
        if(Math.abs(cos) > 1.)  return 0.;
        // 反余弦值
        double acos = Math.acos(cos);
//        System.out.println("acos = " + acos); // 值域[0,π]
//        System.out.println("∠AOB = " + Math.toDegrees(acos)); // 球心角 值域[0,180]
        // 最终结果
        return EARTH_RADIUS * acos;
    }
}
